import java.io.*;
import java.net.*;
import java.util.*;
public class Server
{

    // The ServerSocket we'll use for accepting new connections
    private ServerSocket ss;


    // A mapping from sockets to DataOutputStreams. This will
    // help us avoid having to create a DataOutputStream each time
    // we want to write to a stream.
    private Hashtable<String, Player> players = new Hashtable<String, Player> ();

    // Constructor and while-accept loop all in one.
    public Server (int port) throws IOException
    {
	// All we have to do is listen
	listen (port);
    }


    private void listen (int port) throws IOException
    {

		// Create the ServerSocket
		ss = new ServerSocket (port);
	
		// Tell the world we're ready to go
		System.out.println ("Listening on " + ss);
	
		// Keep accepting connections forever
		while (true)
		{
	
		    // Grab the next incoming connection
		    Socket s = ss.accept ();
	
		    // Tell the world we've got it
		    System.out.println ("Connection from " + s);
	
		    // Create a DataOutputStream for writing data to the
		    // other side
		    DataOutputStream dout = new DataOutputStream (s.getOutputStream ());
		    DataInputStream din = new DataInputStream (s.getInputStream());
		    String user;
		    try
		    {
		    	user = din.readUTF();
		    	if(players.containsKey(user))
		    	{
		    		break;
		    	}
		    }
		    catch(IOException e)
		    {
		    	System.out.println(e);
				break;
		    }
		    
		    Player p = new Player(user, din, dout, s);
		    
		    // Save this stream so we don't need to make it again
		    players.put (user, p);
	
		    sendToAll(p.getUsername() + " has connected!");
		    
		    // Create a new thread for this connection, and then forget
		    // about it
		    new ServerThread (this, p, dout);
		}
    }


    // Get an enumeration of all the OutputStreams, one for each client
    // connected to us
    Enumeration<Player> getPlayers ()
    {
    	return players.elements ();
    }


    // Send a message to all clients (utility routine)
    void sendToAll (String message)
    {
	// We synchronize on this because another thread might be
	// calling removeConnection() and this would screw us up
	// as we tried to walk through the list
		synchronized (players)
		{
		    // For each client ...
		    for (Enumeration<Player> e = getPlayers () ; e.hasMoreElements () ;)
		    {
				Player p = e.nextElement();
				// ... get the output stream ...
				System.out.println("Sending to: " + p.getUsername());
				DataOutputStream dout = p.getDataOutputStream();
				// ... and send the message
				try
				{
				    dout.writeUTF (message);
				}
				catch (IOException ie)
				{
				    System.out.println (ie);
				}
		    }
		}
    }
    
    void messageTo(Player from, String to, String message)
    {
    	Player sendTo = players.get(to);
    	DataOutputStream dout = sendTo.getDataOutputStream();
	try
	{
	    dout.writeUTF("Private message from " + from.getUsername() + ": " + message);
	}
	catch(IOException ie)
	{
	    System.out.println(ie);
	}
    }


    // Remove a socket, and it's corresponding output stream, from our
    // list. This is usually called by a connection thread that has
    // discovered that the connectin to the client is dead.
    void removeConnection (Player p)
    {
	// Synchronize so we don't mess up sendToAll() while it walks
	// down the list of all output streamsa
		synchronized (players)
		{
		    // Tell the world
		    System.out.println ("Removing connection to " + p.getSocket());
		    // Remove it from our hashtable/list
		    players.remove (p);
		    // Make sure it's closed
		    try
		    {
		    	p.getSocket().close ();
		    }
		    catch (IOException ie)
		    {
		    	System.out.println ("Error closing " + p.getSocket());
		    	ie.printStackTrace ();
		    }
		}
    }


    // Main routine
    // Usage: java Server <port>
    static public void main (String args[]) throws Exception
    {
		// Get the port # from the command line
		int port = Integer.parseInt (args [0]);
		// Create a Server object, which will automatically begin
		// accepting connections.
		new Server (port);
    }
}
